#include "gkt_board.h"
#include "gkt_debug.h"
#include "gkt_os.h"
#include "gkt_user.h"
#include "gkt_window.h"
#include "gkt_wdt_ext.h"

#ifdef GKT_CONFIG_WINDOW_USER_THREAD_SAFE
#define WINDOW_USER_THREAD_SAFE	GKT_CONFIG_WINDOW_USER_THREAD_SAFE

#ifdef GKT_CONFIG_WINDOW_USER_MUTEX_TIMEOUT
#define WINDOW_USER_MUTEX_TIMEOUT	GKT_CONFIG_WINDOW_USER_MUTEX_TIMEOUT
#else
#define WINDOW_USER_MUTEX_TIMEOUT	5000
#endif

#else
#define WINDOW_USER_THREAD_SAFE	0
#endif

typedef struct _window_user {
#if WINDOW_USER_THREAD_SAFE
	gkt_mutex_t	mutex;
#endif
	gkt_semaphore_t	semaphore;

	gkt_window_id_t	window_id;
	uint16_t	minor_type;
	uint32_t	user_index;

	uint32_t	type;
	uint32_t	authority;
	const void	*data;
	uint32_t	data_len;

	uint32_t	verified_user_id;
} window_user_s;
static window_user_s s_window_user;

#if WINDOW_USER_THREAD_SAFE
#define WINDOW_USER_MUTEX_INIT()	\
	do {	\
		s_window_user.mutex = gkt_mutex_create(0);	\
		if (!s_window_user.mutex) {	\
			gkt_error("window_user: create mutex failed!\n");	\
			return GKT_ECREATE;	\
		}	\
	} while(0)
#define WINDOW_USER_MUTEX_LOCK()	\
	do {	\
		int __mutex_retval__ = gkt_mutex_lock(s_window_user.mutex, WINDOW_USER_MUTEX_TIMEOUT);	\
		if (__mutex_retval__ != GKT_SUCCESS) {	\
			gkt_error("window_user: mutex lock failed %d\n", __mutex_retval__);	\
			return __mutex_retval__;	\
		}
	} while(0)
	
#define WINDOW_USER_MUTEX_UNLOCK()	\
	gkt_mutex_unlock(s_window_user.mutex)
#else
#define WINDOW_USER_MUTEX_INIT()	do {} while(0)
#define WINDOW_USER_MUTEX_LOCK()	do {} while(0)
#define WINDOW_USER_MUTEX_UNLOCK()	do {} while(0)
#endif

int gkt_window_user_enroll(uint32_t user_index, 
			uint32_t type, const void *data, uint32_t len)
{
	gkt_window_s *window;
	int retval;

	window = gkt_window_find(GKT_WINDOW_ID_CURRENT);
	if (!window) {
		gkt_error("window_user_enroll: current NULL!\n");
		return GKT_EUNKNOWN;
	}

	WINDOW_USER_MUTEX_LOCK();
	if (s_window_user.minor_type != GKT_WINDOW_USER_MAX) {
		WINDOW_USER_MUTEX_UNLOCK();
		gkt_error("window_user_enroll: processing the previous request (%u) ...\n", 
					s_window_user.minor_type);
		return GKT_EBUSY;
	}

	s_window_user.minor_type = GKT_WINDOW_USER_ENROLL;
	s_window_user.window_id = window->desc->id;
	s_window_user.user_index = user_index;
	s_window_user.type = type;
	s_window_user.data = data;
	s_window_user.data_len = len;

	WINDOW_USER_MUTEX_UNLOCK();

	retval = gkt_semaphore_release(s_window_user.semaphore);
	if (__UNLIKELY(retval != GKT_SUCCESS)) {
		gkt_error("window_user_enroll: release semaphore failed %d\n", retval);
		s_window_user.minor_type = GKT_WINDOW_USER_MAX;
		return retval;
	}

	return GKT_EINPROCESS;
}

int gkt_window_user_verify(uint32_t type, 
			uint32_t authority, const void *data, 
			uint32_t len, uint32_t verified_user_id)
{
	gkt_window_s *window;
	int retval;

	window = gkt_window_find(GKT_WINDOW_ID_CURRENT);
	if (!window) {
		gkt_error("window_user_verify: current NULL!\n");
		return GKT_EUNKNOWN;
	}

	WINDOW_USER_MUTEX_LOCK();
	if (s_window_user.minor_type != GKT_WINDOW_USER_MAX) {
		WINDOW_USER_MUTEX_UNLOCK();
		gkt_error("window_user_verify: processing the previous request (%u) ...\n", 
					s_window_user.minor_type);
		return GKT_EBUSY;
	}

	s_window_user.minor_type = GKT_WINDOW_USER_VERIFY;
	s_window_user.window_id = window->desc->id;
	s_window_user.type = type;
	s_window_user.authority = authority;
	s_window_user.data = data;
	s_window_user.data_len = len;
	s_window_user.verified_user_id = verified_user_id;

	WINDOW_USER_MUTEX_UNLOCK();

	retval = gkt_semaphore_release(s_window_user.semaphore);
	if (__UNLIKELY(retval != GKT_SUCCESS)) {
		gkt_error("window_user_verify: release semaphore failed %d\n", retval);
		s_window_user.minor_type = GKT_WINDOW_USER_MAX;
		return retval;
	}

	return GKT_EINPROCESS;
}

void gkt_window_user_verify_update(uint32_t type)
{
	gkt_window_s *window;
	int retval;

	window = gkt_window_find(GKT_WINDOW_ID_CURRENT);
	if (!window) {
		gkt_error("window_user_verify_update: current NULL!\n");
		return;
	}

	WINDOW_USER_MUTEX_LOCK();
	if (s_window_user.minor_type != GKT_WINDOW_USER_MAX) {
		WINDOW_USER_MUTEX_UNLOCK();
		gkt_error("window_user_verify_update: processing the previous request (%u) ...\n", 
					s_window_user.minor_type);
		return;
	}

	s_window_user.minor_type = GKT_WINDOW_USER_VERIFY_UPDATE;
	s_window_user.window_id = window->desc->id;
	s_window_user.type = type;
	s_window_user.data = 0;

	WINDOW_USER_MUTEX_UNLOCK();

	retval = gkt_semaphore_release(s_window_user.semaphore);
	if (__UNLIKELY(retval != GKT_SUCCESS)) {
		gkt_error("window_user_verify_update: release semaphore failed %d\n", retval);
		s_window_user.minor_type = GKT_WINDOW_USER_MAX;
	}
}

static void window_user_notify(int result, uint32_t data)
{
	gkt_event_s event;

	gkt_trace("window_user_notify: result(%d), data(0x%x)\n",
				result, data);

	event.window_id = s_window_user.window_id;
	event.flags = 0;
	event.major_type = GKT_EVENT_USER;
	event.minor_type = s_window_user.minor_type;
	event.data[GKT_WINDOW_USER_EVTDATA_RESULT] = (uint32_t)result;
	event.data[GKT_WINDOW_USER_EVTDATA_DATA] = data;

	s_window_user.minor_type = GKT_WINDOW_USER_MAX;

	gkt_event_put(&event, GKT_INFINITE);
}

static void window_user_routine(void *params)
{
	int retval;
	uint32_t data;

	while (1) {
		gkt_wdte_mark(GKT_WDTE_ID(WINDOW_USER), GKT_WDTE_ST_IDLE);
		retval = gkt_semaphore_acquire(s_window_user.semaphore, 
						GKT_INFINITE);
		gkt_wdte_mark(GKT_WDTE_ID(WINDOW_USER), GKT_WDTE_ST_BUSY);
	
		if (GKT_SUCCESS == retval) {
			data = ~0;
			if (GKT_WINDOW_USER_VERIFY == s_window_user.minor_type) {
				data = s_window_user.verified_user_id;
				retval = gkt_user_verify(&data, 
								s_window_user.type, s_window_user.authority,
								s_window_user.data, s_window_user.data_len);
			}
			else if (GKT_WINDOW_USER_ENROLL == s_window_user.minor_type)
				retval = gkt_user_enroll(s_window_user.type, 
								s_window_user.data, s_window_user.data_len);
			else if (GKT_WINDOW_USER_VERIFY_UPDATE == s_window_user.minor_type) {
				gkt_user_verify_update(s_window_user.type);
				s_window_user.minor_type = GKT_WINDOW_USER_MAX;
				continue;
			}
			else {
				gkt_trace("user_routine: invaid user minor_type(%u)\n", 
							s_window_user.minor_type);
				continue;
			}

			window_user_notify(retval, data);
		}
		else {
			gkt_error("user_routine: acquire semaphore failed %d\n", retval);
		}
	}
}

int gkt_window_user_init(void)
{
	gkt_thread_t thread_id;

	s_window_user.minor_type = GKT_WINDOW_USER_MAX;

	WINDOW_USER_MUTEX_INIT();

	s_window_user.semaphore = gkt_semaphore_create(1, 0);
	if (__UNLIKELY(!s_window_user.semaphore)) {
		gkt_error("window_user_init: create semaphore failed!\n");
		return GKT_ENORESOURCE;
	}

	thread_id = gkt_thread_create(window_user_routine, NULL, 
					GKT_THREAD_PRIORITY_NORMAL, 0x2000, NULL);
	if (__UNLIKELY(!thread_id)) {
		gkt_error("window_user_init: create thread failed!\n");
		return GKT_ENORESOURCE;
	}

	return GKT_SUCCESS;
}

#define gkt_window_user_mask	NULL
#define gkt_window_user_event_handler	NULL

GKT_EVENT_DESC_DEFINE(USER, window_user);

